package com.umpay.utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.umpay.util.StringUtil;
import com.umpay.utils.HashEncrypt;
import com.umpay.utils.PropertiesUtil;


/**
 * 哈希签名帮助类
 * 
 * @author chenkaichao
 * @version 1.0, 2012-07-26
 */
public class PayGateHashSignHelper {

	private static Logger logger = Logger.getLogger(PayGateHashSignHelper.class);

	private static final String ALG_MD5 = "MD5";
	private static final String ALG_SHA1 = "SHA-1";
	private static final String CHAR_SET = "GBK";

	/**
	 * 验证签名
	 * 
	 * @param reqSign
	 *            请求验证的签名
	 * @param params
	 *            签名源map
	 * @param key
	 *            签名静态密钥
	 * @param signType
	 *            签名方式
	 * @param inputCharset
	 *            签名字符集
	 * @return 验签结果 ture：验签正确 false：验签失败
	 */
	public static boolean verifySign(String reqSign, Map<String, Object> params, String key, String signType, String inputCharset) {
		if (StringUtil.isEmpty(reqSign))
			return false;

		String sign = getHashSignMsg(params, key, signType, inputCharset);
		return reqSign.equalsIgnoreCase(sign);
	}

	/**
	 * 验证签名 （默认用sha签名，字符集gbk）
	 * 
	 * @param reqSign
	 *            请求验证的签名
	 * @param params
	 *            签名源map
	 * @param key
	 *            签名静态密钥
	 * @return 验签结果 ture：验签正确 false：验签失败
	 */
	public static boolean verifySign(String reqSign, Map<String, Object> params, String key) {
		return verifySign(reqSign, params, key, ALG_SHA1, CHAR_SET);
	}

	/**
	 * 签名
	 * 
	 * @param params
	 *            签名源map
	 * @param key
	 *            签名静态密钥
	 * @param signType
	 *            签名方式
	 * @param inputCharset
	 *            签名字符集
	 * @return 签名值（hex字符串）
	 */
	public static String getHashSignMsg(Map<String, Object> params, String key, String signType, String inputCharset) {
		String signMsg = "";
		if (ALG_MD5.equals(signType)) {
			signMsg = getMd5SignMsg(params, key, inputCharset);
		} else if (ALG_SHA1.equals(signType)) {
			signMsg = getSha1SignMsg(params, key, inputCharset);
		} else {
			signMsg = getSha1SignMsg(params, key, inputCharset);// 默认用sha-1算法
		}

		return signMsg;
	}

	/**
	 * @param params
	 * @throws UnsupportedEncodingException
	 */
	private static void decodeUrlEncodedFields(Map<String, Object> params) {
		String urlEncodedFiledsString = (String)PropertiesUtil.getBusiStrValue("UrlEncoded.Fielsd","");// URL编码字段
		if (!StringUtil.isEmpty(urlEncodedFiledsString)) {
			String[] urlEncodedFileds = urlEncodedFiledsString.split(",");
			for (String key : urlEncodedFileds) {
				if (!StringUtil.isEmpty(key) || params.containsKey(key)) {
					String urlEncodedValue = (String) params.get(key);
					if (!StringUtil.isEmpty(urlEncodedValue)) {
						try {
							params.put(key, URLDecoder.decode(urlEncodedValue, CHAR_SET));
						} catch (Exception e) {
							logger.error(e.getMessage(), e);
						}
						logger.debug(String.format("URLDecode Key %s", key));
					}
				}
			}
		}
	}

	/**
	 * 删除不需要签名的字段
	 * 
	 * @param params
	 */
	private static void removeExcludeFileds(Map<String, Object> params) {
		String excludeFiledsString = (String)PropertiesUtil.getBusiStrValue("Exclude.Fielsd","");// 不参与Hash的字段
		logger.info("不参与Hash的字段:" + excludeFiledsString);
		if (!StringUtil.isEmpty(excludeFiledsString)) {
			String[] excludeFileds = excludeFiledsString.split(",");
			for (String key : excludeFileds) {
				if (!StringUtil.isEmpty(key)) {
					params.remove(key);
					logger.debug(String.format("Remove Key %s", key));
				}
			}
		}
	}

	/**
	 * md5签名
	 * 
	 * @param params
	 *            签名源map
	 * @param key
	 *            签名静态密钥
	 * @param inputCharset
	 *            签名字符集
	 * @return 签名值（hex字符串）
	 */
	public static String getMd5SignMsg(Map<String, Object> params, String key, String inputCharset) {
		String signSource = getSignSourMsg(params, key);
		String signMsg = HashEncrypt.doEncrypt(signSource, ALG_MD5, inputCharset);
		return signMsg;
	}

	/**
	 * sha1签名
	 * 
	 * @param params
	 *            签名源map
	 * @param key
	 *            签名静态密钥
	 * @param inputCharset
	 *            签名字符集
	 * @return 签名值（hex字符串）
	 */
	public static String getSha1SignMsg(Map<String, Object> params, String key, String inputCharset) {
		String signSource = getSignSourMsg(params, key);
		String signMsg = HashEncrypt.doEncrypt(signSource, ALG_SHA1, inputCharset);
		return signMsg;
	}

	/**
	 * 得到签名源数据
	 * 
	 * @param params
	 *            签名源map
	 * @param key
	 *            静态密钥
	 * @return 签名源数据（prop1=value1&prop2=value2&key=keyValue）
	 */
	private static String getSignSourMsg(Map<String, Object> params, String key) {
		
		//removeExcludeFileds(params);
		decodeUrlEncodedFields(params);
		String signSource = createLinkString(params);
		if (key != null && key.length() > 0) {
			signSource = signSource + "&signKey=" + key;
		}
		return signSource;
		
	}

	/**
	 * 签名字符串,按照key值排序，字母按a-z排序，值为null或空值的不参与签名
	 * 
	 * @param params
	 * @return
	 */
	private static String createLinkString(Map<String, Object> params) {
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		StringBuffer prestr = new StringBuffer("");

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			if(excludeFiledSet.contains(key))
				continue;
			String value = (String) params.get(key);
			if (value != null && value.length() > 0) {				
				prestr.append(key).append("=").append(value).append("&");				
			}
		}
		return prestr.substring(0, prestr.length()-1);
	}

	private static Set<String> excludeFiledSet;
	
	static {
		excludeFiledSet = new HashSet<String>();
		String excludeFiledsString =(String)PropertiesUtil.getBusiStrValue("Exclude.Fielsd","");
		if (!StringUtil.isEmpty(excludeFiledsString)) {
			String[] excludeFileds = excludeFiledsString.split(",");
			for (String key : excludeFileds) {
				if (!StringUtil.isEmpty(key)) {
					excludeFiledSet.add(key);					
				}
			}			
		}
		logger.info("Remove Key %s"+excludeFiledSet);
	}
	public static void main(String[] args) {
		Map<String, Object> s = new HashMap<String, Object>();
		s.put("name", "java");
		s.put("athor", "zhang3");
		s.put("price", "5.5");
		s.put("publish", "");
		s.put("area", null);
		System.out.println(getSha1SignMsg(s, "555555", CHAR_SET));
	}
}
